package net.apexes.commons.lang;

import java.io.ByteArrayOutputStream;

/**
 * @author hedyn
 */
public class Base62 {
    private Base62() {}

    private static final char[] encodes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
    private static final byte[] decodes = new byte[256];

    static {
        for (int i = 0; i < encodes.length; i++) {
            decodes[encodes[i]] = (byte) i;
        }
    }

    /**
     * 将数据编码成 Base62 字符串
     *
     * @param bytes 要编码的数据
     * @return 返回Base62字符串
     */
    public static String encode(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        int pos = 0, val = 0;
        for (byte datum : bytes) {
            val = (val << 8) | (datum & 0xFF);
            pos += 8;
            while (pos > 5) {
                char c = encodes[val >> (pos -= 6)];
                sb.append(c == 'i' ? "ia" : c == '+' ? "ib" : c == '/' ? "ic" : c);
                val &= ((1 << pos) - 1);
            }
        }
        if (pos > 0) {
            char c = encodes[val << (6 - pos)];
            sb.append(c == 'i' ? "ia" : c == '+' ? "ib" :c == '/' ? "ic" : c);
        }
        return sb.toString();
    }

    /**
     * 将 Base62 字符串解码成byte数组
     * @param base62 要解码的 Base62 字符串
     * @return 返回解码后的字节数组
     */
    public static byte[] decode(String base62) {
        if (base62 == null) {
            return null;
        }
        char[] data = base62.toCharArray();
        ByteArrayOutputStream baos = new ByteArrayOutputStream(base62.toCharArray().length);
        int pos = 0, val = 0;
        for (int i = 0; i < data.length; i++) {
            char c = data[i];
            if (c == 'i') {
                c = data[++i];
                c = c == 'a' ? 'i' : c == 'b' ? '+' : c == 'c' ? '/' : data[--i];
            }
            val = (val << 6) | decodes[c];
            pos += 6;
            while (pos > 7) {
                baos.write(val >> (pos -= 8));
                val &= ((1 << pos) - 1);
            }
        }
        return baos.toByteArray();
    }
}
